package com.ybear.ybutils.utils.comparator;

import com.ybear.ybutils.utils.ObjUtils;

import java.util.Comparator;
import java.util.Map;

/**
 * Map排序比较器
 * @param <K>   key类型
 * @param <V>   value类型
 *
 * 用法
 *    List<Map.Entry<K, V>> list = new ArrayList<>( map.entrySet() );
 *    //Key 升序
 *    Comparator<Map.Entry<Long, String>> compare1 = new MapSortComparator<K, V>().keyAsc();
 *    //Key 降序
 *    Comparator<Map.Entry<Long, String>> compare2 = new MapSortComparator<K, V>().keyDesc();
 *    //Value 升序
 *    Comparator<Map.Entry<Long, String>> compare3 = new MapSortComparator<K, V>().valueAsc();
 *    //Value 降序
 *    Comparator<Map.Entry<Long, String>> compare4 = new MapSortComparator<K, V>().valueDesc();
 *
 *    //需要自排序时，调用setKeyComparator 或者 setValueComparator
 *    if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
 *        list.sort( compare1 );
 *    }else {
 *        Collections.sort( list, compare1 );
 *    }
 */
public class MapSortComparator<K, V> implements Comparator<Map.Entry<K, V>> {
    public @interface Type {
        int KEY = 0;
        int VALUE = 1;
    }
    public @interface Order {
        int ASC = 0;
        int DESC = 1;
    }
    @Type
    private int type = Type.KEY;
    @Order
    private int order = Order.ASC;

    private Comparator<K> keyComparator;
    private Comparator<V> valComparator;

    public MapSortComparator() {}
    public MapSortComparator(@Type int type, @Order int order) {
        this.type = type;
        this.order = order;
    }

    public MapSortComparator<K, V> keyAsc() {
        this.type = Type.KEY;
        this.order = Order.ASC;
        return this;
    }

    public MapSortComparator<K, V> keyDesc() {
        this.type = Type.KEY;
        this.order = Order.DESC;
        return this;
    }

    public MapSortComparator<K, V> valueAsc() {
        this.type = Type.VALUE;
        this.order = Order.ASC;
        return this;
    }

    public MapSortComparator<K, V> valueDesc() {
        this.type = Type.VALUE;
        this.order = Order.DESC;
        return this;
    }

    public MapSortComparator<K, V> setKeyComparator(Comparator<K> keyComparator) {
        this.keyComparator = keyComparator;
        return this;
    }

    public MapSortComparator<K, V> setValComparator(Comparator<V> valComparator) {
        this.valComparator = valComparator;
        return this;
    }

    @Override
    public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
        switch ( type ) {
            case Type.KEY:
                K ko1 = o1.getKey();
                K ko2 = o2.getKey();
                switch ( order ) {
                    case Order.ASC:
                        if( keyComparator != null ) return keyComparator.compare( ko1, ko2 );
                        return compareTo( ko1, ko2 );
                    case Order.DESC:
                        if( keyComparator != null ) return keyComparator.compare( ko2, ko1 );
                        return compareTo( ko2, ko1 );
                    default:
                        return 0;
                }
            case Type.VALUE:
                V vo1 = o1.getValue();
                V vo2 = o2.getValue();
                switch ( order ) {
                    case Order.ASC:
                        if( valComparator != null ) return valComparator.compare( vo1, vo2 );
                        return compareTo( vo1, vo1);
                    case Order.DESC:
                        if( valComparator != null ) return valComparator.compare( vo2, vo1 );
                        return compareTo( vo2, vo1 );
                    default:
                        return 0;
                }
            default:
                return 0;
        }
    }

    private int compareTo(Object o1, Object o2) {
        if( o1 == null || o2 == null ) return 0;
        if( o1 instanceof String || o2 instanceof String ) {
            return ObjUtils.parseString( o1 ).compareTo( ObjUtils.parseString( o2 ) );
        }
        double do1 = ObjUtils.parseDouble( o1 );
        double do2 = ObjUtils.parseDouble( o2 );
        if( do1 != 0 && do2 != 0 ) return Double.compare( do1, do2 );
        return 0;
    }

}